﻿#region Copyright(c) 2006 ZO, All right reserved.
// -----------------------------------------------------------------------------
// Copyright(c) 2006 ZO, All right reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   1.  Redistribution of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//   2.  Redistribution in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//   3.  The name of the author may not be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// -----------------------------------------------------------------------------
#endregion


#region Using directives

using System;
using System.Net;
using System.Runtime.Remoting.Messaging;
using ArgumentNullException= ZO.SmartCore.Core.ArgumentNullException;

#endregion

namespace ZO.SmartCore.Net.Sockets
{
    /// <summary>
    /// Encapsulation and caching of IP address information. Very similar to <see cref="System.Net.IPEndPoint"/>,
    /// but adds async DNS lookups. 
    /// </summary>
    public sealed class Address
    {

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="Address"/> class.
        /// </summary>
        /// <param name="server">The server ipaddress or dns name of remote machine.</param>
        /// <param name="port">The port.</param>
        public Address(string server, int port)
            : this()
        {
            this.Host = server;

            if (port >= 0)
            {
                this.Port = port;
            }

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Address"/> class.
        /// </summary>
        public Address()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Address"/> class.
        /// </summary>
        /// <param name="ip">The ip.</param>
        /// <param name="port">The port.</param>
        public Address(IPAddress ip, int port)
            : this()
        {
            this.IP = ip;
            if (port >= 0)
            {
                this.Port = port;
            }
        }
        #endregion

        #region Destructor

        #endregion


        #region Delegates
        private delegate void AsyncResolve();
        #endregion


        #region Fields
        private int _Port;
        private IPAddress _IP;
        private string _Host;
        private IPEndPoint _Endpoint;
        private string _Domain;
        private bool _IsResolved;
        #endregion

        #region Events
        /// <summary>
        /// Raised When Address is Resolved;
        /// </summary>
        public event EventHandler Resolved;
        #endregion

        #region Operators

        #endregion

        #region Properties
        
        /// <summary>
        /// Gets or set the domain name of the Ip Address.
        /// </summary>
        public string Domain
        {
            get
            {
                if (this.IsResolved && !string.IsNullOrEmpty(_Domain))
                {
                    return _Domain;
                }
                return this._Host;

            }
            set
            {
                this._Domain = value;
            }
        }




        /// <summary>
        /// Gets or sets The host IP address.
        /// </summary>
        /// <remarks>It can be the IP Address at which socket start listening</remarks>
        public IPAddress IP
        {
            get
            {
                return this._IP;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("IP");
                }
                else if (this._IP != value)
                {
                    IsResolved = false;
                    this._IP = value;
                    this._Host = null;
                }
            }
        }



        /// <summary>
        /// Gets or sets the remote port.
        /// </summary>
        /// <remarks>It can be the port at which socket start listening</remarks>
        public int Port
        {
            get
            {
                return this._Port;
            }
            set
            {
                if (value > 0)
                {
                    this._Port = value;
                }
                else
                {
                    this._Port = 0;
                }
                IsResolved = false;


            }
        }

        /// <summary>
        /// Gets or sets the the remote host.
        /// </summary>
        /// <remarks>It can be the name of host at which socket start listening</remarks>
        public string Host
        {
            get
            {
                return this._Host;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("Server");
                }
                else if (this._Host != value)
                {

                    if (!IPAddress.TryParse(value, out this._IP))
                    {
                        this._Host = value;
                        this._IP = null;
                    }
                    else
                    {
                        this._Host = null;
                    }
                    IsResolved = false;

                }
            }
        }

        /// <summary>
        ///  Get an IPEndPoint for making socket connections with or listening. 
        /// </summary>
        /// <value>The ip endpoint.</value>
        public IPEndPoint Endpoint
        {
            get
            {
                return _Endpoint;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this address is resolved.
        /// </summary>
        /// <value>
        /// 	<see langword="true"/> if this address is resolved; otherwise, <see langword="false"/>.
        /// </value>
        public bool IsResolved
        {
            get
            {
                return _IsResolved;
            }
           private  set
            {
                _IsResolved = value;
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Resolves the DNS host name or IP address to an IPHostEntry instance.
        /// </summary>
        public void Resolve()
        {
            if (!IsResolved)
            {
                //Establish the remote endpoint for the socket.
                IPHostEntry ipHostInfo;
                if (String.IsNullOrEmpty(this.Host))
                {
                    ipHostInfo = Dns.GetHostEntry(this.IP);
                    this._IP = ipHostInfo.AddressList[0];
                    this._Host = ipHostInfo.HostName;
                }
                else
                {
                    ipHostInfo = Dns.GetHostEntry(this.Host);
                    this._IP = ipHostInfo.AddressList[0];
                }
                this._Endpoint = new IPEndPoint(this.IP, this.Port);

                if (this.Resolved != null)
                {
                    this.Resolved(this, EventArgs.Empty);
                }
                IsResolved = true;
            }
        }

    
    

        /// <summary>Begins an asynchronous request Resolves the DNS host name or IP address to an IPHostEntry instance.</summary>
        /// <returns>An <see cref="T:System.IAsyncResult"></see> that references the asynchronous connection.</returns>
        /// <param name="callback">The <see cref="T:System.AsyncCallback"></see> delegate. </param>
        /// <param name="state">An object that contains state information for this request. </param>
        public IAsyncResult BeginResolve(AsyncCallback callback, object state)
        {
            AsyncResolve resolver = new AsyncResolve(this.Resolve);

            return resolver.BeginInvoke(callback, state);
        }

        /// <summary>Ends an asynchronous request for DNS information.</summary>
        /// <returns>An <see cref="T:System.IAsyncResult"></see> that references the asynchronous connection.</returns>
        /// <param name="asyncResult">An <see cref="T:System.IAsyncResult"></see> instance that 
        /// is returned by a call to the <see cref="BeginResolve"></see> method.</param>
         public void EndResolve(IAsyncResult asyncResult)
        {
            AsyncResolve resolver = (AsyncResolve)((AsyncResult)asyncResult).AsyncDelegate; 


            resolver.EndInvoke(asyncResult);
        }

        /// <summary>
        /// Readable representation of the address. 
        /// </summary>
        public override string ToString()
        {
            return Endpoint.ToString();
        }
        #endregion
    }
}
